🎓 Maestría en Inteligencia Artificial Aplicada¶
🖼️ Visión Computacional para Imágenes y Video¶
👨🏫 Profesores¶
- Profesor Titular: Dr. Gilberto Ochoa Ruiz
- Profesor Asistente: MIP Ma. del Refugio Melendez Alfaro
- Profesor Tutor: M. en C. Jose Angel Martinez Navarro
Actividad 9.4: Google Colab con algoritmo Otsu¶
📌 Detalles de la Actividad¶
- Código: 9.4 Google Colab
- Título: Algoritmo Otsu
- Fecha de entrega: 📅 Nov 9, 2025 a las 23:59
- Formato de entrega: ZIP
- Modalidad: Equipo
👥 Team 13¶
🚀 Nuestro Equipo¶
Javier Augusto Rebull Saucedo¶
Matrícula: |
Juan Carlos Pérez Nava¶
Matrícula: |
Luis Gerardo Sánchez Salazar¶
Matrícula: |
Oscar Enrique García García¶
Matrícula: |
Objetivo del Proyecto¶
El objetivo de esta sesión práctica es implementar el algoritmo de segmentación de Otsu, así como conocer sus fortalezas y debilidades
📚 Tabla de Contenidos¶
💻 Tecnologías Utilizadas¶
- Python 3.8+ - Lenguaje de programación
- Skimage - Para procesamiento de imágenes
- NumPy - Computación numérica
- Matplotlib / Seaborn - Visualización de datos
- Google Colab - Entorno de ejecución
- gdown - Descarga de datasets desde Google Drive
Importación de Librerías ¶
📦 Carga de Librerías y Configuración del Entorno¶
Este bloque inicial prepara el entorno para procesamiento y visualización de imágenes, así como para el manejo de archivos en diferentes formatos y la obtención de información del sistema.
🔍 Librerías Principales¶
matplotlib.pyplot
Se utiliza para mostrar imágenes, histogramas y resultados de procesamiento visual.numpy
Manejo de arreglos y operaciones numéricas optimizadas, indispensables para el tratamiento de imágenes.
🖼️ Procesamiento de Imágenes y Umbralización¶
skimage.data
Proporciona imágenes de ejemplo para pruebas rápidas.threshold_otsuythreshold_multiotsu
Métodos de umbralización automática.- Otsu calcula un umbral óptimo para separar la imagen en fondo y objeto.
- Multi-Otsu extiende el método para segmentar en múltiples clases.
try_all_threshold
Permite comparar visualmente diferentes métodos de umbralización.imageio.v3yPIL.Image
Lectura y manipulación de imágenes en distintos formatos.
🖼️ Soporte para Formato HEIC/HEIF¶
pillow-heif
Se instala y registra para permitir la carga de imágenes .heic y .heif de forma directa con PIL.
🛠️ Utilidades del Sistema¶
gdown
Descarga archivos desde Google Drive.os,sys,socket,platform,pathlib
Proporcionan acceso al sistema operativo, entorno de ejecución y rutas.datetimeyZoneInfo
Se utilizan para imprimir una marca de tiempo personalizada con zona horaria.
✅ Mensaje Final¶
Se imprime:
- Confirmación de que las librerías han sido cargadas.
- La fecha y hora del sistema con zona horaria
America/Mexico_City. - Información sobre país y región del entorno (si hay conexión a internet).
- Versión de Python, ejecutable, plataforma, hostname, carpeta activa y usuario.
Esto ayuda a documentar el contexto de ejecución, lo cual es útil para reproducir resultados en distintos entornos.
# ===============================================================
# Librerías para Visualización y Procesamiento Numérico
# ===============================================================
import matplotlib.pyplot as plt # Biblioteca para generar gráficos y visualizar resultados de procesamiento
import numpy as np # Biblioteca para operaciones numéricas y manejo de arrays
import re # Biblioteca para manejo de expresiones regulares y procesamiento de texto
# ===============================================================
# Librerías para Procesamiento de Imágenes y Umbralización
# ===============================================================
!pip install pillow-heif # Instala soporte para imágenes HEIF/HEIC
!pip -q install opencv-python-headless # Instala OpenCV sin componentes gráficos
from skimage import data # Carga imágenes de ejemplo
from skimage.filters import threshold_otsu # Umbralización automática (Otsu)
from skimage.filters import threshold_multiotsu # Umbralización multiclase (más de dos niveles)
from skimage.filters import try_all_threshold # Prueba varios métodos de umbralización
import cv2 # Procesamiento de imágenes y visión por computadora
import imageio.v3 as iio # Lectura y escritura de imágenes en distintos formatos
from PIL import Image # Manipulación y visualización de imágenes
from pillow_heif import register_heif_opener # Permite abrir imágenes HEIC/HEIF en PIL
register_heif_opener() # Activa el soporte HEIF/HEIC en PIL
# ===============================================================
# UTILIDADES Y MANEJO DEL SISTEMA
# ===============================================================
import gdown # Descarga de archivos desde Google Drive
import os # Operaciones del sistema operativo
import sys # Parámetros y funciones específicas del sistema
import socket # Interfaz de red de bajo nivel
import platform # Información sobre la plataforma
import pathlib # Manejo orientado a objetos de rutas
from datetime import datetime # Manejo de fechas y horas
from zoneinfo import ZoneInfo # Manejo de zonas horarias
# ===============================================================
# MENSAJE DE CONFIRMACIÓN
# ===============================================================
print("✅ Librerías cargadas")
print(f"📅 Timestamp de ejecución: {datetime.now(ZoneInfo('America/Mexico_City')).strftime('%Y-%m-%d %H:%M:%S')}")
try:
with urlopen('https://ipapi.co/country_name/') as response:
country = response.read().decode('utf-8').strip()
print(f"🌍 País de ejecución: {country}")
with urlopen('https://ipapi.co/region/') as response:
region = response.read().decode('utf-8').strip()
print(f"🗺️ Estado/Región de ejecución: {region}")
except Exception as e:
print("No se pudo determinar el país o estado de ejecución (posiblemente sin conexión a internet).")
print("Python:", sys.version)
print("Ejecutable:", sys.executable)
print("Plataforma:", platform.platform())
print("Hostname:", socket.gethostname())
print("Carpeta trabajo:", os.getcwd())
print("Usuario:", pathlib.Path.home())
Collecting pillow-heif Downloading pillow_heif-1.1.1-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl.metadata (9.6 kB) Requirement already satisfied: pillow>=11.1.0 in /usr/local/lib/python3.12/dist-packages (from pillow-heif) (11.3.0) Downloading pillow_heif-1.1.1-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl (5.5 MB) ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 5.5/5.5 MB 32.3 MB/s eta 0:00:00 Installing collected packages: pillow-heif Successfully installed pillow-heif-1.1.1 ✅ Librerías cargadas 📅 Timestamp de ejecución: 2025-10-30 20:54:13 No se pudo determinar el país o estado de ejecución (posiblemente sin conexión a internet). Python: 3.12.12 (main, Oct 10 2025, 08:52:57) [GCC 11.4.0] Ejecutable: /usr/bin/python3 Plataforma: Linux-6.6.105+-x86_64-with-glibc2.35 Hostname: 13b6db1eb6b4 Carpeta trabajo: /content Usuario: /root
📂 Gestión de Carpetas y Descarga de Imágenes¶
Este bloque realiza dos tareas principales:
- Limpieza de directorios para asegurar que no existan archivos residuales.
- Descarga de imágenes desde Google Drive hacia carpetas locales organizadas.
🧹 1. Limpieza de Directorios Existentes¶
Se define una lista de carpetas que pueden contener imágenes o datos previos.
Para cada carpeta:
- Se verifica si existe.
- Si existe, se eliminan todos los archivos y subcarpetas internos.
- Si no existe, simplemente se omite.
Esto asegura que los resultados previos no interfieran con el nuevo procesamiento.
🔧 Se utiliza
os.unlink()para archivos yshutil.rmtree()para subdirectorios.
🗂️ 2. Preparación del Directorio de Descarga¶
Se crea la carpeta imagenes_nuevas si no está presente.
Esta carpeta almacenará las imágenes descargadas en su formato original.
⬇️ 3. Descarga de Imágenes desde Google Drive¶
Se define un diccionario donde:
- La clave es el nombre del archivo destino.
- El valor es el ID del archivo en Google Drive.
Luego, se usa gdown.download() para descargar cada imagen de forma automática.
🌁 4. Descarga Adicional en la Carpeta bay¶
Se repite el mismo procedimiento, pero esta vez para la carpeta bay, destinada a otro conjunto de imágenes.
Si la carpeta no existe, se crea.
Luego se descargan las imágenes usando el mismo esquema de ID.
✅ Resultado¶
Al finalizar, el entorno queda con:
| Carpeta | Contenido |
|---|---|
imagenes_nuevas/ |
Primer grupo de imágenes descargadas |
bay/ |
Segundo grupo de imágenes descargadas |
Resto de carpetas (data/, p2_*) |
Limpias y listas para procesamiento |
# ===============================================================
# Limpiar todos los archivos en las carpetas especificadas
# ===============================================================
print("\n--- Limpiando archivos en las carpetas especificadas ---")
directorios_a_limpiar = ["imagenes_nuevas", "data", "p2_coins", "p2b_plates", "p2c_wheresimba", "bay"]
for directorio in directorios_a_limpiar:
if os.path.exists(directorio):
print(f"Eliminando contenido de la carpeta '{directorio}'...")
for filename in os.listdir(directorio):
file_path = os.path.join(directorio, filename)
try:
if os.path.isfile(file_path) or os.path.islink(file_path):
os.unlink(file_path) # Eliminar archivo o enlace simbólico
elif os.path.isdir(file_path):
shutil.rmtree(file_path) # Eliminar subcarpetas
except Exception as e:
print(f"Error al eliminar {file_path}: {e}")
print(f"Carpeta '{directorio}' limpiada.")
else:
print(f"La carpeta '{directorio}' no existe, no requiere limpieza.")
# ===============================================================
# PARTE 1: DESCARGA LISTA DE IMÁGENES
# ===============================================================
# --- 1. Preparar el directorio de salida original ---
output_dir_original = "imagenes_nuevas"
if not os.path.exists(output_dir_original):
os.makedirs(output_dir_original)
print(f"Carpeta '{output_dir_original}' creada.")
# --- 2. Definir las imágenes a descargar ---
imagenes_a_descargar = {
"01 - Cerro de a Silla Monterrey.HEIC": "1anUZCFwSnZII15MDqGb5sApYhmoXqx18",
"02 - The Killers Las Vegas.HEIC": "1iizEGyZRAQJ-zcu4MOMdo_OATKvQ8HMr",
"03 - Bellagio at Night.HEIC": "1u7N9au9fdDat9GYgK215SrSYfWVMLvSK",
"04 - The Great Canyon.heic": "1zmudHsvMzDmITmQljkq7MOeAoaJj4gxo",
"05 - The Cosmopolitan Las Vegas.HEIC": "1JymKRxsTuiIeuviO9RwotmQqRJ838V-m",
"06 - Chinese Dragon.heic": "1rdBFw1yUmmXQqFca6iUppbYgSgzGorge",
"07 - The Park Las Vegas.HEIC": "1BJD1hWP17PsF7pelR7zCW91sNb5cK31m",
"08 - Simba Snow.HEIC": "1BOLxQGbKCZtRwA6wVyCBaKgowZfBrJGD"
}
print("\n--- Iniciando la descarga de la lista de imágenes ---")
# --- 3. Bucle para descargar cada imagen de la lista ---
for filename, file_id in imagenes_a_descargar.items():
output_path = os.path.join(output_dir_original, filename)
print(f"Descargando '{filename}'...")
gdown.download(id=file_id, output=output_path, quiet=False)
# --- 4. Se repiten los pasos 1, 2 y 3 para otra carpeta de imagenes ---
output_dir_original = "bay"
if not os.path.exists(output_dir_original):
os.makedirs(output_dir_original)
print(f"Carpeta '{output_dir_original}' creada.")
imagenes_a_descargar = {
"bay_coittower.heic": "1iFjGeiMmJukD8olvg9hLiGt6MHo7doGh",
"bay_ferry.heic": "1gvdXLQwSGkQ8S2zb-NARRmX_in4fJwMy",
"bay_goldengate.jpeg": "14mICfajLYyKzaVGH9zUZkuAmTo8QT-7j",
"bay_lifebuoy.jpeg": "1rkrXljdLPA7NpuA-GtLbFcFrzX2ns1O4",
"bay_sailboats.jpeg": "1Qq4cMtXN6dnUSMdBj2PMn_oc5vzcRvvM",
"bay_sanfrancisco.heic": "1z-QuwqPhShxBBqwH7VG5i3dniEutLgdS",
"bay_sausalito.jpeg": "19Rsp858V4zbi5nxqiOl6dKJ3kRdTr8vd",
"bay_seagull.heic": "1acKuzQN9w6R5KmhoFEKINP5nCw507ZHw",
"bay_skyline": "16w_Vj7byo5uiFJhIMO5dipvPXqKbqL-E",
"bay_sunset": "1augsryFJdJYtByVxarHImjJByZAvUBL1",
"bay_wheel": "1JJDEUG20SKoOTyeBRIViWgKunVnHlMW_"
}
for filename, file_id in imagenes_a_descargar.items():
output_path = os.path.join(output_dir_original, filename)
print(f"Descargando '{filename}'...")
gdown.download(id=file_id, output=output_path, quiet=False)
print("\n✅ ¡Descarga de la lista completada!")
--- Limpiando archivos en las carpetas especificadas --- La carpeta 'imagenes_nuevas' no existe, no requiere limpieza. La carpeta 'data' no existe, no requiere limpieza. La carpeta 'p2_coins' no existe, no requiere limpieza. La carpeta 'p2b_plates' no existe, no requiere limpieza. La carpeta 'p2c_wheresimba' no existe, no requiere limpieza. La carpeta 'bay' no existe, no requiere limpieza. Carpeta 'imagenes_nuevas' creada. --- Iniciando la descarga de la lista de imágenes --- Descargando '01 - Cerro de a Silla Monterrey.HEIC'...
Downloading... From: https://drive.google.com/uc?id=1anUZCFwSnZII15MDqGb5sApYhmoXqx18 To: /content/imagenes_nuevas/01 - Cerro de a Silla Monterrey.HEIC 100%|██████████| 1.40M/1.40M [00:00<00:00, 99.1MB/s]
Descargando '02 - The Killers Las Vegas.HEIC'...
Downloading... From: https://drive.google.com/uc?id=1iizEGyZRAQJ-zcu4MOMdo_OATKvQ8HMr To: /content/imagenes_nuevas/02 - The Killers Las Vegas.HEIC 100%|██████████| 2.06M/2.06M [00:00<00:00, 49.0MB/s]
Descargando '03 - Bellagio at Night.HEIC'...
Downloading... From: https://drive.google.com/uc?id=1u7N9au9fdDat9GYgK215SrSYfWVMLvSK To: /content/imagenes_nuevas/03 - Bellagio at Night.HEIC 100%|██████████| 4.16M/4.16M [00:00<00:00, 53.0MB/s]
Descargando '04 - The Great Canyon.heic'...
Downloading... From: https://drive.google.com/uc?id=1zmudHsvMzDmITmQljkq7MOeAoaJj4gxo To: /content/imagenes_nuevas/04 - The Great Canyon.heic 100%|██████████| 1.37M/1.37M [00:00<00:00, 44.1MB/s]
Descargando '05 - The Cosmopolitan Las Vegas.HEIC'...
Downloading... From: https://drive.google.com/uc?id=1JymKRxsTuiIeuviO9RwotmQqRJ838V-m To: /content/imagenes_nuevas/05 - The Cosmopolitan Las Vegas.HEIC 100%|██████████| 4.16M/4.16M [00:00<00:00, 113MB/s]
Descargando '06 - Chinese Dragon.heic'...
Downloading... From: https://drive.google.com/uc?id=1rdBFw1yUmmXQqFca6iUppbYgSgzGorge To: /content/imagenes_nuevas/06 - Chinese Dragon.heic 100%|██████████| 3.49M/3.49M [00:00<00:00, 183MB/s]
Descargando '07 - The Park Las Vegas.HEIC'...
Downloading... From: https://drive.google.com/uc?id=1BJD1hWP17PsF7pelR7zCW91sNb5cK31m To: /content/imagenes_nuevas/07 - The Park Las Vegas.HEIC 100%|██████████| 4.88M/4.88M [00:00<00:00, 147MB/s]
Descargando '08 - Simba Snow.HEIC'...
Downloading... From: https://drive.google.com/uc?id=1BOLxQGbKCZtRwA6wVyCBaKgowZfBrJGD To: /content/imagenes_nuevas/08 - Simba Snow.HEIC 100%|██████████| 1.79M/1.79M [00:00<00:00, 150MB/s]
Carpeta 'bay' creada. Descargando 'bay_coittower.heic'...
Downloading... From: https://drive.google.com/uc?id=1iFjGeiMmJukD8olvg9hLiGt6MHo7doGh To: /content/bay/bay_coittower.heic 100%|██████████| 1.18M/1.18M [00:00<00:00, 108MB/s]
Descargando 'bay_ferry.heic'...
Downloading... From: https://drive.google.com/uc?id=1gvdXLQwSGkQ8S2zb-NARRmX_in4fJwMy To: /content/bay/bay_ferry.heic 100%|██████████| 2.19M/2.19M [00:00<00:00, 149MB/s]
Descargando 'bay_goldengate.jpeg'...
Downloading... From: https://drive.google.com/uc?id=14mICfajLYyKzaVGH9zUZkuAmTo8QT-7j To: /content/bay/bay_goldengate.jpeg 100%|██████████| 768k/768k [00:00<00:00, 103MB/s]
Descargando 'bay_lifebuoy.jpeg'...
Downloading... From: https://drive.google.com/uc?id=1rkrXljdLPA7NpuA-GtLbFcFrzX2ns1O4 To: /content/bay/bay_lifebuoy.jpeg 100%|██████████| 601k/601k [00:00<00:00, 58.5MB/s]
Descargando 'bay_sailboats.jpeg'...
Downloading... From: https://drive.google.com/uc?id=1Qq4cMtXN6dnUSMdBj2PMn_oc5vzcRvvM To: /content/bay/bay_sailboats.jpeg 100%|██████████| 725k/725k [00:00<00:00, 87.0MB/s]
Descargando 'bay_sanfrancisco.heic'...
Downloading... From: https://drive.google.com/uc?id=1z-QuwqPhShxBBqwH7VG5i3dniEutLgdS To: /content/bay/bay_sanfrancisco.heic 100%|██████████| 1.73M/1.73M [00:00<00:00, 139MB/s]
Descargando 'bay_sausalito.jpeg'...
Downloading... From: https://drive.google.com/uc?id=19Rsp858V4zbi5nxqiOl6dKJ3kRdTr8vd To: /content/bay/bay_sausalito.jpeg 100%|██████████| 883k/883k [00:00<00:00, 97.0MB/s]
Descargando 'bay_seagull.heic'...
Downloading... From: https://drive.google.com/uc?id=1acKuzQN9w6R5KmhoFEKINP5nCw507ZHw To: /content/bay/bay_seagull.heic 100%|██████████| 938k/938k [00:00<00:00, 77.3MB/s]
Descargando 'bay_skyline'...
Downloading... From: https://drive.google.com/uc?id=16w_Vj7byo5uiFJhIMO5dipvPXqKbqL-E To: /content/bay/bay_skyline 100%|██████████| 1.63M/1.63M [00:00<00:00, 138MB/s]
Descargando 'bay_sunset'...
Downloading... From: https://drive.google.com/uc?id=1augsryFJdJYtByVxarHImjJByZAvUBL1 To: /content/bay/bay_sunset 100%|██████████| 771k/771k [00:00<00:00, 95.0MB/s]
Descargando 'bay_wheel'...
Downloading... From: https://drive.google.com/uc?id=1JJDEUG20SKoOTyeBRIViWgKunVnHlMW_ To: /content/bay/bay_wheel 100%|██████████| 2.37M/2.37M [00:00<00:00, 153MB/s]
✅ ¡Descarga de la lista completada!
Umbralización única ¶
🎯 Umbralización con el Método de Otsu¶
La umbralización es una técnica de segmentación que convierte una imagen en escala de grises en una imagen binaria, separando regiones según la intensidad de los píxeles.
El método de Otsu ✨ automatiza esta decisión al buscar el umbral que mejor distingue dos clases de píxeles: fondo y objeto.
📊 Principio Estadístico¶
Otsu define un criterio de optimización basado en:
- Minimizar la varianza intra-clase (las clases sean internamente homogéneas)
- Maximizar la varianza inter-clase (las clases sean lo más distintas posible)
En términos simples: se elige el umbral que separa mejor el histograma de la imagen.
🧮 Fórmulas Clave¶
Probabilidad de clase:
- $\omega_0(t)$ → proporción de píxeles con intensidad ≤ $t$
- $\omega_1(t)$ → proporción de píxeles con intensidad > $t$
Media de intensidad en cada clase:
- $\mu_0(t)$ → media de intensidades de la clase 0
- $\mu_1(t)$ → media de intensidades de la clase 1
Varianza inter-clase:
$\sigma_b^2(t) = \omega_0(t) \cdot \omega_1(t) \cdot [\mu_0(t) - \mu_1(t)]^2$
👉 El umbral óptimo es aquel que maximiza $\sigma_b^2(t)$.
✅ Condiciones de Aplicabilidad¶
- Ideal para imágenes con histograma bimodal, donde los objetos y el fondo tienen intensidades claramente diferenciadas.
- Menos efectivo en imágenes con ruido, iluminación desigual o múltiples regiones de interés.
| Algoritmo | Tipo | Principio | Ideal para… |
|---|---|---|---|
| Otsu | Automático, único | Maximiza la varianza entre clases | Imágenes con histograma bimodal |
| Li | Automático, único | Minimiza la entropía cruzada | Imágenes con ruido o gradientes suaves |
| Yen | Automático, único | Maximiza la entropía de la imagen | Segmentación con contraste alto |
| Triangle | Automático, único | Geometría del histograma | Distribuciones asimétricas |
| Mean | Manual, único | Promedio de intensidades | Casos simples o controlados |
| Minimum | Automático, único | Busca mínimos locales en el histograma | Imágenes con múltiples modos |
| Isodata | Iterativo, único | Promedia clases hasta converger | Distribuciones complejas |
| Multi-Otsu | Automático, múltiple | Extiende Otsu a múltiples clases | Segmentación en más de dos regiones |
🔎 En resumen, Otsu es una técnica eficiente y totalmente automática, pero su desempeño depende de la calidad del contraste y la distribución tonal de la imagen.
# Cargar imagen de ejemplo
image = data.camera()
# Calcular umbral óptimo con Otsu
thresh = threshold_otsu(image)
binary = image > thresh
# Crear figura con 3 columnas
fig, axes = plt.subplots(ncols=3, figsize=(12, 4))
ax = axes.ravel()
# Imagen original
ax[0].imshow(image, cmap='gray')
ax[0].set_title('Imagen original')
ax[0].axis('off')
# Histograma con línea de umbral
ax[1].hist(image.ravel(), bins=256, color='blue', alpha=0.7)
ax[1].axvline(thresh, color='red', linestyle='--', linewidth=2, label=f'Umbral = {thresh:.0f}')
ax[1].set_title('Histograma de intensidades')
ax[1].set_xlabel('Intensidad')
ax[1].set_ylabel('Frecuencia')
ax[1].legend()
# Imagen umbralizada
ax[2].imshow(binary, cmap='gray')
ax[2].set_title('Imagen binarizada (Otsu)')
ax[2].axis('off')
# Ajustar espaciado
plt.tight_layout()
plt.show()
🔍 Comparación Visual de Métodos de Umbralización¶
Este fragmento de código permite comparar visualmente múltiples algoritmos de umbralización aplicados a una imagen en escala de grises, utilizando la función try_all_threshold.
Es especialmente útil cuando se quiere elegir el mejor método sin necesidad de conocer en detalle las bases matemáticas de cada algoritmo.
El resultado es una rejilla de imágenes, cada una mostrando cómo segmenta la imagen un método distinto.
🎛️ Diferencias Entre Métodos de Umbralización¶
Cada método tiene suposiciones diferentes sobre la distribución de intensidades en la imagen:
| Método | Supuesto / Característica | Comentario |
|---|---|---|
| Otsu | Histograma bimodal | Funciona bien cuando fondo y objeto son claramente distintos. |
| Li / Yen | Optimización basada en entropía o energía | Útiles cuando el contraste no es evidente. |
| Triangle | Basado en la geometría del histograma | Puede ser más robusto ante ruido o asimetrías. |
| Mean | Simple promedio global | Rápido pero menos preciso si la iluminación no es uniforme. |
🎯 ¿Por qué usar try_all_threshold?¶
- Permite comparar métodos lado a lado.
- Facilita la selección informada del mejor enfoque.
- Ahorra tiempo cuando la imagen presenta variaciones de iluminación o ruido.
- Útil en procesamiento exploratorio o análisis inicial de dataset.
✨ En resumen, esta herramienta es ideal para experimentar y observar cómo distintos algoritmos interpretan la misma imagen, permitiendo elegir el umbral más consistente con los objetivos de segmentación.
# Cargar imagen
img = data.page()
# Aplicar todos los métodos de umbralización disponibles
fig, ax = try_all_threshold(img, figsize=(14, 10), verbose=False)
for a in ax:
a.set_title(a.get_title(), fontsize=10)
a.axis('off')
a.images[0].set_cmap('gray')
plt.tight_layout()
plt.show()
Umbralización múltiple ¶
🎨 Segmentación Multiclase con Multi-Otsu¶
Multi-Otsu es una extensión del método clásico de Otsu (1979) que permite segmentar una imagen en más de dos clases mediante la obtención de múltiples umbrales óptimos.
Mientras el Otsu tradicional determina un solo umbral para dividir la imagen en fondo vs objeto, Multi-Otsu generaliza el criterio para K clases, calculando K − 1 umbrales que dividen el histograma en regiones estadísticamente diferenciables.
🧠 Idea Principal¶
El algoritmo selecciona los umbrales que maximizan la varianza inter-clase total, es decir:
- Cada clase debe ser internamente homogénea.
- Las clases deben ser lo más distintas posible entre sí en términos de intensidad.
Esto es especialmente útil para imágenes con múltiples regiones relevantes, como tonos de piel, niveles de iluminación, o diferentes materiales en una escena.
🧮 Formulación¶
Para una imagen con L niveles de gris y K clases, se buscan $K - 1$ umbrales $t_1, t_2, ..., t_{K-1}$ que maximicen:
$\sigma_b^2 = \sum_{k=1}^{K} \omega_k (\mu_k - \mu_T)^2$
donde:
| Símbolo | Significado |
|---|---|
| $\omega_k$ | probabilidad (frecuencia relativa) de la clase k |
| $\mu_k$ | media de intensidades de la clase k |
| $\mu_T$ | media global de toda la imagen |
✅ ¿Cuándo usar Multi-Otsu?¶
| Situación | Adecuado |
|---|---|
| La imagen contiene 3 o más regiones claramente diferenciables | ✔️ Sí |
| La iluminación es relativamente uniforme | ✔️ Sí |
| La imagen es ruidosa o poco contrastada | ⚠️ Puede degradarse el desempeño |
✨ En resumen, Multi-Otsu permite segmentación multiclase automática y estadísticamente óptima, ideal para análisis donde la imagen contiene varias regiones de interés, sin necesidad de definir umbrales manualmente.
# Cargar imagen
image = data.camera()
# Aplicar Multi-Otsu para generar tres clases
thresholds = threshold_multiotsu(image)
# Clasificar píxeles según los umbrales obtenidos
regions = np.digitize(image, bins=thresholds)
# Crear figura con tres subgráficos
fig, ax = plt.subplots(nrows=1, ncols=3, figsize=(14, 4))
titles = ['Imagen original', 'Histograma con umbrales', 'Segmentación Multi-Otsu']
# Imagen original
ax[0].imshow(image, cmap='gray')
ax[0].set_title(titles[0], fontsize=12)
ax[0].axis('off')
# Histograma con líneas de umbral
ax[1].hist(image.ravel(), bins=256, color='steelblue', alpha=0.7)
for thresh in thresholds:
ax[1].axvline(thresh, color='darkred', linestyle='--', linewidth=2, label=f'Umbral = {thresh}')
ax[1].set_title(titles[1], fontsize=12)
ax[1].set_xlabel('Intensidad')
ax[1].set_ylabel('Frecuencia')
ax[1].legend()
# Imagen segmentada con mapa de color perceptual
ax[2].imshow(regions, cmap='jet')
ax[2].set_title(titles[2], fontsize=12)
ax[2].axis('off')
plt.tight_layout()
plt.show()
🌉 Aplicación de Umbralización en Imágenes Reales y Personalizadas¶
En esta sección se retoma el flujo de trabajo desarrollado anteriormente para realizar umbralización y segmentación sobre imágenes en escala de grises.
Inicialmente, se trabajó con imágenes de ejercicios previos (por ejemplo, escenas urbanas y arte en Las Vegas).
Ahora, se amplía el análisis incorporando fotografías capturadas desde la Bahía de San Francisco, tomadas durante el recorrido en ferry desde el Muelle 1 hasta Sausalito y de regreso 🚢.
Estas nuevas imágenes incluyen vistas icónicas como:
- El Golden Gate Bridge 🌉,
- El skyline de San Francisco 🏙️,
- Barcos y velas en la bahía ⛵,
- Elementos marítimos como salvavidas y muelles,
- La zona de Sausalito y sus paisajes costeros.
El objetivo es observar cómo los métodos de Otsu y Multi-Otsu se comportan en escenas con variaciones de iluminación, reflejos, texturas y regiones complejas, comparadas con imágenes más controladas.
La siguiente función visualizar_resultados() permite analizar y comparar el efecto de aplicar Otsu y Multi-Otsu sobre una imagen en escala de grises.
Su objetivo es mostrar, de manera visual y clara, cómo cambian tanto los umbrales como el resultado de la segmentación.
La figura generada presenta 5 paneles que resumen el proceso completo:
🔍 Desglose de la Visualización¶
| Panel | Contenido | Descripción |
|---|---|---|
| 1. Imagen original | cmap='gray' |
Muestra la imagen en escala de grises tal como fue cargada. Sirve como referencia inicial. |
| 2. Histograma + Umbral (Otsu) | axvline(umbral) |
Se grafica el histograma de intensidades y se marca el umbral óptimo calculado con Otsu. Permite observar si la distribución es bimodal. |
| 3. Imagen binaria | cmap='gray' |
Después de aplicar el umbral, se genera una imagen blanco/negro que separa objeto y fondo. Resultado directo de Otsu. |
| 4. Histograma Multiclase (Multi-Otsu) | Líneas verticales múltiples | Se vuelven a graficar las intensidades, pero ahora se marcan múltiples umbrales (K−1) obtenidos con Multi-Otsu. |
| 5. Imagen segmentada multiclase | cmap='jet' |
La imagen se divide en regiones diferenciadas por color, asignando una etiqueta (clase) a cada pixel. Esto permite distinguir más de dos grupos visualmente. |
🎯 Propósito del Ejemplo¶
Esta visualización permite:
- Comparar fácilmente la segmentación binaria vs multiclase.
- Observar la relación entre el histograma y los umbrales obtenidos.
- Identificar regiones dentro de la imagen que podrían representar materiales, niveles de iluminación, texturas o áreas de interés.
- Tomar decisiones sobre qué técnica es más adecuada para el problema.
✨ Resultado Esperado¶
Se obtiene una representación clara del proceso completo de segmentación:
- Del espacio de intensidades → al umbral → a regiones clasificadas.
Esto facilita la interpretación del comportamiento de los algoritmos y ayuda en tareas como detección, análisis de patrones y segmentación semántica básica.
def visualizar_resultados(imagen_array, umbral, imagen_binaria, thresholds, regions):
fig, ax = plt.subplots(nrows=1, ncols=5, figsize=(18, 4))
# 1) Imagen original en escala de grises
ax[0].imshow(imagen_array, cmap='gray')
ax[0].set_title("Imagen original", fontsize=11)
ax[0].axis('off')
# 2) Histograma con umbral simple
ax[1].hist(imagen_array.ravel(), bins=256, color='steelblue', alpha=0.7)
ax[1].axvline(umbral, color='darkred', linestyle='--', linewidth=2, label=f'Umbral = {umbral:.2f}')
ax[1].set_title("Histograma (Otsu)", fontsize=11)
ax[1].set_xlabel("Intensidad")
ax[1].set_ylabel("Frecuencia")
ax[1].legend()
# 3) Imagen binaria (usar cmap='gray')
ax[2].imshow(imagen_binaria, cmap='gray')
ax[2].set_title("Imagen binaria", fontsize=11)
ax[2].axis('off')
# 4) Histograma con multi-umbrales
ax[3].hist(imagen_array.ravel(), bins=256, color='steelblue', alpha=0.7)
for t in thresholds:
ax[3].axvline(t, color='darkred', linestyle='--', linewidth=2)
ax[3].set_title("Histograma (Multi-Otsu)", fontsize=11)
ax[3].set_xlabel("Intensidad")
# 5) Imagen segmentada por múltiples regiones usando 'jet'
ax[4].imshow(regions, cmap='jet')
ax[4].set_title("Regiones (Multi-Otsu)", fontsize=11)
ax[4].axis('off')
plt.tight_layout()
plt.show()
🔄 Flujo de Procesamiento en Cada Imagen¶
Para cada ruta en la lista:
- Se lee la imagen en formato RGB (
iio.imread). - Se convierte a escala de grises (
convert("L")). - Se obtiene el arreglo numérico (
np.array). - Se aplica Otsu para obtener un umbral binario.
- Se aplica Multi-Otsu para obtener múltiples umbrales.
- Se segmenta la imagen en regiones etiquetadas (
np.digitize). - Se visualizan los resultados completos con
visualizar_resultados().
rutas_imagenes = [
os.path.join("imagenes_nuevas", "05 - The Cosmopolitan Las Vegas.HEIC"),
os.path.join("imagenes_nuevas", "06 - Chinese Dragon.heic"),
os.path.join("imagenes_nuevas", "07 - The Park Las Vegas.HEIC"),
os.path.join("bay", "bay_coittower.heic"),
os.path.join("bay", "bay_ferry.heic"),
os.path.join("bay", "bay_goldengate.jpeg"),
os.path.join("bay", "bay_lifebuoy.jpeg"),
os.path.join("bay", "bay_sailboats.jpeg"),
os.path.join("bay", "bay_sanfrancisco.heic"),
os.path.join("bay", "bay_sausalito.jpeg"),
os.path.join("bay", "bay_seagull.heic"),
os.path.join("bay", "bay_skyline"),
os.path.join("bay", "bay_sunset"),
os.path.join("bay", "bay_wheel")
]
for ruta in rutas_imagenes:
imagen_rgb = iio.imread(ruta)
imagen_pil = Image.fromarray(imagen_rgb)
imagen_gris = imagen_pil.convert("L")
imagen_array = np.array(imagen_gris)
umbral = threshold_otsu(imagen_array)
imagen_binaria = imagen_array > umbral
thresholds = threshold_multiotsu(imagen_array)
regions = np.digitize(imagen_array, bins=thresholds)
visualizar_resultados(imagen_array, umbral, imagen_binaria, thresholds, regions)
🎯 Resultado y Observación¶
Este conjunto de pruebas permite comparar el desempeño de los algoritmos en diferentes contextos visuales:
- Escenas de interior vs. exterior
- Iluminación artificial vs. iluminación natural
- Objetos con bordes definidos vs. paisajes con transiciones suaves
- Composiciones simples vs. entornos complejos como la bahía de San Francisco
El contraste entre estos casos ayuda a evaluar la sensibilidad y las limitaciones de la segmentación automática, mostrando cómo el método puede comportarse de manera diferente según la textura, contraste y distribución tonal de la imagen.
Este análisis también abre la puerta a mejoras futuras, como:
- Aplicar filtros de suavizado o reducción de ruido antes de segmentar,
- Realizar ecualización de histograma para mejorar el contraste,
- Explorar segmentación basada en textura o patrones, especialmente útil en paisajes naturales y escenas urbanas detalladas.
🧭 Comparación Global de Métodos de Umbralización¶
Además de aplicar Otsu y Multi-Otsu, se ejecuta una comparación amplia con try_all_threshold para probar automáticamente múltiples algoritmos sobre la misma imagen.
Esto se aplicó tanto a imágenes de ejercicios anteriores como a las capturadas durante el recorrido en ferry por la Bahía de San Francisco (vistas del Golden Gate, el skyline, muelles y embarcaciones) 🚢🌉.
🔍 Qué hace ahora el flujo¶
- Se ejecuta
try_all_thresholdsin mostrar la figura temporal (modo no interactivo). - Se extraen los resultados (título y binarización) de cada método.
- Se cierran todas las figuras pendientes para evitar que Colab muestre la rejilla 2×4.
- Se crea una única figura final en formato 1×8 por imagen, con todos los métodos alineados horizontalmente.
Resultado: por cada imagen se obtiene una sola fila con 8 paneles comparables (mismo cmap='gray', títulos normalizados y ejes ocultos).
🎯 Valor del análisis¶
| Beneficio | Descripción |
|---|---|
| Comparación directa | Una fila horizontal 1×8 facilita ver diferencias entre métodos en la misma escala visual. |
| Selección informada | Permite elegir el algoritmo más estable ante reflejos, sombras y texturas reales. |
| Control en Colab | Evita figuras duplicadas y asegura una única salida limpia por imagen. |
✨ En conjunto, esta configuración proporciona una vista clara y compacta del comportamiento de los algoritmos de umbralización, adecuada para evaluar escenas urbanas y marítimas con variaciones reales de iluminación y contraste.
for ruta in rutas_imagenes:
print(f"\nProcesando: {ruta}")
# --- Cargar y a gris ---
imagen_rgb = iio.imread(ruta)
imagen_pil = Image.fromarray(imagen_rgb)
imagen_gris = imagen_pil.convert("L")
imagen_array = np.array(imagen_gris)
# --- 1) Ejecutar try_all_threshold SIN mostrar nada ---
with plt.ioff():
fig_tmp, ax_tmp = try_all_threshold(imagen_array, figsize=(12, 8), verbose=False)
# Aplanar ejes y extraer (título, imagen)
axes_list = np.array(ax_tmp).ravel().tolist()
results = [(a.get_title(), a.images[0].get_array()) for a in axes_list]
results = results[:8] # fuerza 8 métodos
n_methods = len(results)
# --- 2) Cerrar cualquier figura pendiente para que Colab no la muestre ---
plt.close('all')
# --- 3) Crear la figura final: 1 fila x 8 columnas ---
fig, axes = plt.subplots(nrows=1, ncols=n_methods, figsize=(3*n_methods, 3))
if n_methods == 1:
axes = [axes]
for ax, (title, img_bin) in zip(axes, results):
ax.imshow(img_bin, cmap='gray', interpolation='nearest')
ax.set_title(title, fontsize=9)
ax.axis('off')
fig.suptitle(f"Comparación de Umbralización (1x{n_methods}) - {os.path.basename(ruta)}", fontsize=12)
# --- 4) Mostrar figura ---
plt.tight_layout()
plt.show()
Procesando: imagenes_nuevas/05 - The Cosmopolitan Las Vegas.HEIC
Procesando: imagenes_nuevas/06 - Chinese Dragon.heic
Procesando: imagenes_nuevas/07 - The Park Las Vegas.HEIC
Procesando: bay/bay_coittower.heic
Procesando: bay/bay_ferry.heic
Procesando: bay/bay_goldengate.jpeg
Procesando: bay/bay_lifebuoy.jpeg
Procesando: bay/bay_sailboats.jpeg
Procesando: bay/bay_sanfrancisco.heic
Procesando: bay/bay_sausalito.jpeg
Procesando: bay/bay_seagull.heic
Procesando: bay/bay_skyline
Procesando: bay/bay_sunset
Procesando: bay/bay_wheel
🎯 Mini-Proyecto: Detección y Segmentación de Códigos QR utilizando Umbralización de Otsu¶
📌 Introducción¶
Los códigos QR (Quick Response) son patrones bidimensionales que almacenan información mediante módulos en blanco y negro. Se utilizan ampliamente debido a su capacidad de codificar datos de forma compacta y ser leídos rápidamente, incluso desde cámaras de baja calidad 📷.
Estos códigos pueden contener:
- 🌐 URLs
- 📝 Texto (mensajes, nombres, instrucciones)
- 👤 Información de contacto (vCards)
- 🏷️ Identificadores de objetos o activos
- 🔗 Referencias para sistemas logísticos o industriales
Para procesarlos de manera eficaz, es importante lograr una separación clara entre el patrón del código y el fondo. Aquí es donde el método de Otsu resulta muy útil.
⚙️ ¿Por qué Otsu para Códigos QR?¶
El método de Otsu determina automáticamente el umbral óptimo para convertir una imagen en escala de grises en una imagen binaria. Esto ayuda a resaltar los módulos (cuadrados) del QR con claridad.
✅ Ventajas clave:¶
- Sin parámetros manuales → ideal para automatización.
- Bajo costo computacional → apto para dispositivos móviles o sistemas embebidos.
- Maximiza la separación estadística entre fondo y patrón.
- Mejora la robustez en la extracción de contornos para que el QR pueda ser decodificado correctamente.
En otras palabras, Otsu ayuda a producir una imagen binaria limpia, lo cual facilita la detección y lectura del código QR 🔍.
🧪 Casos de Uso incluidos en este mini-proyecto¶
Se utilizarán tres ejemplos prácticos para demostrar diferentes escenarios:
1) 🟩 QR Limpio Generado en Página Web¶
- Imagen con un código QR nítido y fondo uniforme.
- Contenido: un link a una página web.
- Objetivo: demostrar el flujo base →
imagen → escala de grises → Otsu → binarización → detección → decodificación.
2) 🏷️ Varios Códigos QR con Nombres del Equipo¶
- Imagen con múltiples códigos QR, cada uno con el nombre de un integrante.
- Caso práctico: marcar objetos personales (celulares, cargadores, laptops).
- Permite observar cómo Otsu puede separar correctamente varios códigos en la misma escena.
3) 🤖 Código QR en Entorno Industrial¶
- Imagen de una parte de un robot industrial identificada mediante QR.
- Aplicación común:
trazabilidad de componentes, control de inventario, mantenimiento programado. - Aquí se observarán desafíos como:
- Iluminación variable 💡
- Reflejos metálicos ✨
- Texturas complejas 🔩
🎯 Objetivo del Mini-Proyecto¶
- Aplicar Otsu como método de preprocesamiento para mejorar la lectura y reconocimiento de códigos QR.
- Analizar cómo cambia el desempeño al pasar de escenarios controlados a escenarios reales.
- Relacionar la binarización con aplicaciones prácticas en:
- Identidad digital personal
- Organización de objetos
- Industria y manufactura inteligente
🏁 Resultado Esperado¶
| Habilidad | Resultado |
|---|---|
| 🧠 Comprender Otsu | Saber cuándo y por qué usarlo en imágenes para QR |
| 🖼️ Preprocesar imágenes | Convertir imágenes reales a binario de alta calidad |
| 🔍 Decodificar QR | Extraer correctamente la información contenida |
| 🏭 Aplicar a contexto real | Identificar casos donde esto mejora procesos logísticos e industriales |
✨ En resumen, Otsu no solo convierte una imagen a blanco y negro:
permite estructurar la información visual de una manera útil para la identificación, trazabilidad y automatización.
🔧 Implementación¶
Se agregaron 4 funciones para manejar imágenes y detectar códigos QR. Primero se descargan📥 los recursos desde Google Drive, esta vez desde una carpeta, en caso de que sean muchas imágenes para analizar. Luego se enumeran🗂️ los archivos disponibles, después se detectan y decodifican 🔍 los códigos QR presentes en las imágenes, y finalmente se visualizan 🎨 resaltando su posición y contenido. De esta manera, se cubre el proceso completo: adquisición → organización → análisis → presentación.
download_drive_folder_public(folder_url_or_id, dest_dir) 📁⬇️¶
Descarga una carpeta pública completa de Google Drive (usando URL o ID) y guarda todo su contenido en dest_dir.
Retorno: No regresa valor; solo descarga y confirma la ruta.
list_folder_contents(base_folder) 🗂️¶
Recorre una carpeta y genera una lista ordenada de las rutas de todos los archivos dentro de ella (incluye subcarpetas).
Retorno: list[str] con rutas de archivos.
decode_qr_all(img_uint8) 🔍📦¶
Detecta y decodifica uno o múltiples códigos QR en una imagen dada. Si la detección múltiple falla, intenta detección simple.
Retorno: [(texto_decodificado, coordenadas_esquinas), ...].
draw_qr_boxes(ax, base_img_rgb, results) 🎨🧩¶
Muestra la imagen original y dibuja cuadros y etiquetas sobre cada código QR detectado, usando sus coordenadas.
Retorno: No regresa valor; modifica la visualización en el ax.
def download_drive_folder_public(folder_url_or_id: str, dest_dir: str):
import gdown
os.makedirs(dest_dir, exist_ok=True)
# Accept URL or raw ID
if re.search(r'/folders/([a-zA-Z0-9_-]{10,})', folder_url_or_id):
url = folder_url_or_id
else:
url = f"https://drive.google.com/drive/folders/{folder_url_or_id}"
# remaining_ok allows partial downloads to resume
gdown.download_folder(url=url, output=dest_dir, quiet=False, use_cookies=False, remaining_ok=True)
print(f"Done. Files saved under: {os.path.abspath(dest_dir)}")
def list_folder_contents(base_folder: str) -> list:
file_list = []
for root, _, files in os.walk(base_folder):
for f in files:
relative_path = os.path.join(root, f)
file_list.append(relative_path)
return sorted(file_list)
def decode_qr_all(img_uint8):
det = cv2.QRCodeDetector()
results = []
try:
retval, decoded_info, points, _ = det.detectAndDecodeMulti(img_uint8)
if retval and points is not None:
for txt, pts in zip(decoded_info, points):
if txt:
results.append((txt, pts))
except Exception:
pass
if not results:
txt, pts, _ = det.detectAndDecode(img_uint8)
if txt:
results.append((txt, pts))
return results
def draw_qr_boxes(ax, base_img_rgb, results):
ax.imshow(base_img_rgb)
ax.axis('off')
for txt, pts in results:
if pts is None:
continue
pts = pts.reshape(-1, 2).astype(int)
poly = np.vstack([pts, pts[0]])
ax.plot(poly[:,0], poly[:,1], linewidth=2)
ax.text(pts[0,0], pts[0,1]-5, txt[:40], fontsize=9, bbox=dict(facecolor='white', alpha=0.6))
Se descargan automáticamente las imágenes desde una carpeta pública en Google Drive 📥 y luego se enumeran todas las rutas de los archivos descargados para poder procesarlos posteriormente 🗂️.
# Descargando las imagenes de la carpeta
download_drive_folder_public(
"https://drive.google.com/drive/folders/1CbcVoFIuJg0VZiR26RXPDyObQK14Djci?usp=drive_link",
"/content/qr"
)
# Enlistando las imagenes descargadas
rutas_imagenes = list_folder_contents("qr")
Retrieving folder contents
Processing file 1I4cOoT-1iQbc1tg8hesw5ARFy-9CeXu4 qrcode1.png Processing file 1QpxBMNUCGM_6182tWV8YSMFBriOHgT7_ qrcode3.HEIC Processing file 1pzq1psm095LtAmTbDLX_g8B60PB1Nqkp qrcode4.HEIC
Retrieving folder contents completed Building directory structure Building directory structure completed Downloading... From: https://drive.google.com/uc?id=1I4cOoT-1iQbc1tg8hesw5ARFy-9CeXu4 To: /content/qr/qrcode1.png 100%|██████████| 27.7k/27.7k [00:00<00:00, 22.2MB/s] Downloading... From: https://drive.google.com/uc?id=1QpxBMNUCGM_6182tWV8YSMFBriOHgT7_ To: /content/qr/qrcode3.HEIC 100%|██████████| 2.48M/2.48M [00:00<00:00, 104MB/s] Downloading... From: https://drive.google.com/uc?id=1pzq1psm095LtAmTbDLX_g8B60PB1Nqkp To: /content/qr/qrcode4.HEIC 100%|██████████| 2.18M/2.18M [00:00<00:00, 172MB/s]
Done. Files saved under: /content/qr
Download completed
Se aplica el método de Otsu para binarizar cada imagen y luego se detectan los códigos QR presentes 🔍. Finalmente, se muestran los resultados visualmente y se imprime el contenido encontrado 🖼️📢.
# Se aplica el flujo de trabajo
for ruta in rutas_imagenes:
# Aplicar OTSU
imagen_rgb = iio.imread(ruta)
imagen_pil = Image.fromarray(imagen_rgb)
imagen_gris = imagen_pil.convert("L")
imagen_array = np.array(imagen_gris)
umbral = threshold_otsu(imagen_array)
imagen_binaria = (imagen_array > umbral).astype(np.uint8) * 255
#Extraer QRs sobre imagen binaria (OTSU)
results = decode_qr_all(imagen_binaria)
# Desplegar imagenes
print(f"*"*100)
print(f"Analizando archivo {ruta}")
print(f"*"*100)
plt.figure(figsize=(12,5))
plt.subplot(1,2,1)
plt.imshow(imagen_binaria, cmap='gray')
plt.title('Imagen Binaria (OTSU)')
plt.axis('off')
plt.subplot(1,2,2)
draw_qr_boxes(plt.gca(), imagen_rgb, results)
plt.title('Detección QR (sobre original)')
plt.show()
# Imprimir resultados
if results:
print(f"*"*100)
print(f"CÓDIGOS QR ENCONTRADOS!")
print(f"*"*100)
for i, (txt, _) in enumerate(results, 1):
print(f" >>>[{i}] {txt}")
else:
print(f"{ruta}: No se encontraron códigos QR")
print("\n" * 3)
**************************************************************************************************** Analizando archivo qr/qrcode1.png ****************************************************************************************************
**************************************************************************************************** CÓDIGOS QR ENCONTRADOS! **************************************************************************************************** >>>[1] https://myqrcode.mobi/fc69d759 **************************************************************************************************** Analizando archivo qr/qrcode3.HEIC ****************************************************************************************************
**************************************************************************************************** CÓDIGOS QR ENCONTRADOS! **************************************************************************************************** >>>[1] OSCAR GARCIA >>>[2] LUIS SANCHEZ >>>[3] JUAN CARLOS PEREZ >>>[4] JAVIER REBULL **************************************************************************************************** Analizando archivo qr/qrcode4.HEIC ****************************************************************************************************
**************************************************************************************************** CÓDIGOS QR ENCONTRADOS! **************************************************************************************************** >>>[1] PART_ID:178431/DESCRIPTION:FANUC_ROBOT_MOTOR_AXIS2
✅ Reflexión del Mini-Proyecto¶
Se mostró cómo la umbralización con el método de Otsu puede funcionar como un paso clave para la detección y lectura confiable de códigos QR. Otsu permitió separar de forma automática las zonas oscuras y claras de cada imagen, creando una versión binaria más estable para el algoritmo de decodificación. Esto facilitó la lectura tanto en códigos QR limpios, como en escenas con múltiples códigos, e incluso en contextos industriales, donde las condiciones de iluminación y textura pueden variar.
La práctica demostró que el uso de QR tiene una amplia aplicación, desde compartir enlaces y etiquetar objetos personales, hasta la trazabilidad y gestión de componentes industriales. El método de Otsu resulta especialmente útil porque no requiere definir manualmente un umbral, lo que hace posible automatizar el proceso en sistemas reales, móviles o embebidos. En resumen, Otsu fortalece la robustez y simplifica la etapa de preprocesamiento en tareas de reconocimiento visual con códigos QR.
🎯 Conclusiones¶
Esta práctica permitió explorar, aplicar y comparar distintos métodos de umbralización y segmentación sobre imágenes reales, abarcando tanto escenas controladas como contextos visualmente complejos.
🔬 Comprensión de Umbralización Binaria y Multiclase¶
- Se reforzó el entendimiento del método de Otsu, observando su capacidad para separar imagen en dos clases maximizando la diferencia estadística entre fondo y objeto.
- Se extendió este criterio con Multi-Otsu, el cual permitió segmentar imágenes en múltiples niveles, mostrando cómo regiones de diferente luminancia pueden ser clasificadas sin intervención manual.
- Esto permitió comprender que la estructura del histograma es crítica para la efectividad de ambos métodos.
🌁 Aplicación en Imágenes Reales con Variabilidad Lumínica¶
Las imágenes capturadas durante el recorrido en ferry por la Bahía de San Francisco presentaron:
- Reflexiones intensas en el agua 🌊
- Neblina o difusión atmosférica 🌫️
- Zonas arquitectónicas de alto contraste 🌉
- Elementos de detalle fino (barcos, mástiles, texturas)
Esto permitió observar que:
| Condición de la Imagen | Efecto Observado |
|---|---|
| Alto contraste definido | Otsu genera segmentaciones estables y limpias |
| Gradientes suaves o neblina | Multi-Otsu identifica rangos intermedios más útiles |
| Superficies con reflejos | Algunos métodos basados en entropía (Li, Yen) producen segmentaciones más equilibradas |
| Texturas irregulares (agua) | Métodos simples como Mean o Triangle tienden a fragmentar resultados |
Esto demuestra que la selección del método depende del contenido visual, no solo del nivel de gris global.
🧭 Comparación Sistemática con try_all_threshold¶
La comparación permitió:
- Evaluar sensibilidad de cada método ante la iluminación.
- Observar diferencias en términos de agresividad de la segmentación.
- Detectar casos donde un método falla, por ejemplo:
MinimumyTrianglepueden sobsegmentar en escenas con fuerte reflejo.LiyYensuelen preservar zonas suaves, produciendo separaciones más naturales.IsodatayOtsufuncionan mejor cuando el histograma tiene dos masas diferenciadas.
Esto confirma que no existe un método universalmente óptimo, sino adecuación al contexto.
🧩 Integración con el Mini-Proyecto de QR¶
El mini-proyecto de detección de códigos QR permitió trasladar los conceptos de umbralización a un caso de uso práctico, donde la segmentación binaria es un paso previo esencial para el reconocimiento visual. El método de Otsu demostró ser especialmente útil al producir una binarización automática y consistente, lo cual facilitó la extracción de los patrones modulares característicos del QR. Con ello fue posible detectar y decodificar códigos en tres escenarios distintos: un QR limpio generado digitalmente, una imagen con múltiples QR utilizados para identificación personal, y un código adherido a una pieza en contexto industrial, donde la trazabilidad juega un papel clave.
Este ejercicio reforzó la relevancia de Otsu en aplicaciones donde la separación entre figura y fondo es necesaria para etapas posteriores de análisis, clasificación o decodificación. Además, permitió observar cómo su simplicidad computacional lo hace adecuado para sistemas reales, como cámaras móviles, robots industriales o lectores embarcados, donde se requiere procesar imágenes sin intervención humana constante. En síntesis, el mini-proyecto evidenció que la umbralización binaria no solo es una herramienta teórica, sino un componente central en flujos de visión artificial orientados a identificación, registro y automatización.
Conclusiones Personales¶
🎓 Javier Augusto Rebull Saucedo:
El método de Otsu me resultó especialmente útil porque elimina la necesidad de seleccionar manualmente un umbral. Para mí, la clave es que se basa en una función de optimización clara: minimizar la varianza intra-clase. En escenarios donde se requiere tomar decisiones binarias sobre patrones (por ejemplo, detección de zonas de firma en documentos digitalizados), esta automatización es valiosa siempre que el histograma tenga una separación razonable.
🎓 Juan Carlos Pérez Nava:
La propiedad más importante de Otsu para mí es que no requiere parámetros de entrada, lo que lo hace reproducible. Sin embargo, la práctica me confirma que la eficacia depende fuertemente del contraste entre fondo y región de interés. En imágenes médicas donde hay gradientes suaves o tejidos con intensidades similares, se vuelve necesario combinar Otsu con normalización o ecualización previa.
🎓 Oscar Enrique García García:
Ver Otsu aplicado sobre imágenes naturales refuerza su rol como técnica de segmentación no supervisada basada en estadística básica. Lo considero útil como etapa de preprocesamiento antes de modelos más complejos, especialmente cuando se busca reducir dimensionalidad a regiones relevantes. Sin embargo, es evidente que su desempeño decae cuando la escena no presenta bimodalidad clara, lo cual justifica exploraciones multiclase o basadas en textura.
🎓 Luis Gerardo Sánchez Salazar:
En aplicaciones industriales donde se requiere inspección visual, Otsu es atractivo por ser computacionalmente ligero y determinista. Para detectar bordes de piezas, orificios o zonas de desgaste sobre superficies metálicas, su utilidad es tangible cuando el contraste está bien definido. Pero cuando la iluminación no es controlada o hay reflejos, el método necesita soporte adicional, como iluminación fija o filtros previos, para mantener estabilidad en la segmentación.
✅ Conclusión General¶
En conjunto, esta práctica permitió comprender, comparar y aplicar distintos métodos de umbralización y segmentación en imágenes reales, destacando cómo la forma del histograma, la iluminación y el contenido visual influyen fuertemente en el resultado. Métodos como Otsu y Multi-Otsu resultaron especialmente útiles para separar figura y fondo de manera automática, mientras que la comparación con try_all_threshold demostró que no existe un método único ideal, sino que la elección depende del contexto. Finalmente, la integración con la detección de códigos QR mostró cómo estas técnicas son fundamentales en aplicaciones reales de visión artificial orientadas a identificación y automatización. 🔍🤖✨
📚 Referencias¶
Barron, J. T. (2020). A generalization of Otsu’s method and minimum error thresholding. arXiv. Recuperado de https://arxiv.org/pdf/2007.07350
Chen, R., Yu, Y., Xu, X., Wang, L., Zhao, H., & Tan, H.-Z. (2019). Adaptive binarization of QR code images for fast automatic sorting in warehouse systems. Sensors, 19(24), 5466. https://doi.org/10.3390/s19245466
Chen, R., Li, W., Lan, K., Xiao, J., Wang, L., & Lu, X. (2023). Fast adaptive binarization of QR code images for automatic sorting in logistics systems. Electronics, 12(2), 286. https://doi.org/10.3390/electronics12020286
Data Carpentry. (2024). Image processing tutorial: Thresholding. Recuperado de https://datacarpentry.github.io/image-processing/07-thresholding.html
GeeksforGeeks. (2025, agosto 18). Python | Thresholding techniques using OpenCV – Set-3 (Otsu thresholding). Recuperado de https://www.geeksforgeeks.org/python/python-thresholding-techniques-using-opencv-set-3-otsu-thresholding/
Gonzalez, R. C., & Woods, R. E. (2018). Digital image processing (4.ª ed.). Pearson.
Karrach, L., Pivarčiová, E., & Božek, P. (2020). Identification of QR code perspective distortion based on edge directions and edge projections analysis. Journal of Imaging, 6(7), 67. https://pmc.ncbi.nlm.nih.gov/articles/PMC8321072/
Murzova, A. (2020, agosto 5). Otsu’s thresholding with OpenCV. LearnOpenCV. Recuperado de https://learnopencv.com/otsu-thresholding-with-opencv/
OpenCV. (2025, abril 23). Image thresholding using OpenCV. OpenCV Blog. Recuperado de https://opencv.org/blog/image-thresholding-using-opencv/
OpenCV. (s. f.). Image Thresholding (OpenCV-Python Tutorials). Recuperado de https://docs.opencv.org/4.x/d7/d4d/tutorial_py_thresholding.html
scikit-image. (2024). Multi-Otsu thresholding. Recuperado de https://scikit-image.org/docs/stable/auto_examples/segmentation/plot_multiotsu.html
scikit-image. (2024). Thresholding — example gallery. Recuperado de https://scikit-image.org/docs/stable/auto_examples/segmentation/plot_thresholding.html
Tutorialspoint. (s. f.). Multi-Otsu Thresholding (scikit-image). Recuperado de https://www.tutorialspoint.com/scikit-image/scikit-image-multi-otsu-thresholding.htm
Wikipedia. (s. f.). Otsu's method. Recuperado de https://en.wikipedia.org/wiki/Otsu%27s_method